#include "world.hpp"
#include "Ai.hpp"
#include <iostream>
#include <cmath>

using namespace std;

map_chunk::map_chunk(): map_elm(), fruit(NULL), building(NULL), area_qualif(area_none), valeurSucre(0),danger_estimation(0),value_estimation(0){}
map_chunk::~map_chunk(){};

//ComputeValue
#define COEF_SOURCE         1               //Coef de la case
#define COEF_RADIATION      0.5             //Coef des cases autour
#define IMPACT_NBR_BALLE    0.9             //Pourcentage d'impact du nombre de balles sur la valeur du stuff

//ComputeDanger
#define INFLUENCE_DANGERDIRECT      100
#define INFLUENCE_DANGERINDIRECT    75

//Constantes arbitraires de l'influence des bâtiments
#define CONSTANTE_ARB_BUILDING_VITAMIN_SOURCE       400
#define CONSTANTE_ARB_BUILDING_SUGAR_TREE           630
#define CONSTANTE_ARB_BUILDING_JUICE_BARREL         220
#define CONSTANTE_ARB_BUILDING_SUGAR_BOWL           220
#define CONSTANTE_ARB_BUILDING_FRUCTIFICATION_TANK  220

#define INFLUENCE_DANGERDIRECT      50

#define RAYON_INFLUENCE_MOB         5


int givePrice(int objet);                   //Crache le prix d'achat d'un objet donné, référencé par sa constante issue de FruitSaladAi.hpp
int giveAmmo(int objet);                    //Idem pour le nombre de "munitions"

void World::computeValue()
{
    unsigned int chunkValue;
    float coefCase;

    for(unsigned int iX = 0; iX < width;iX++){
        for(unsigned int iY = 0; iY < height;iY++){
                chunkValue = 0;

                for(unsigned int iVoisX = -1; iVoisX <= 1; iVoisX++){
                    for(unsigned int iVoisY = -1; iVoisY <= 1; iVoisY++){
                        if(exists(iVoisX,iVoisY))
                        {
                            (iVoisX == 0 && iVoisY == 0) ? coefCase = COEF_SOURCE : coefCase = COEF_RADIATION; //Coef adéquat pour la case (case "source" ou case "iradiée")

                            //Valeur liée au stuff présent
                            map_chunk chunk = at(iVoisX,iVoisY);
                            for( vector<Object*>::iterator it = chunk.objects.begin(); it < chunk.objects.end(); it++ ){
                                //Coef de la case * prix de l'équipement * ((100% - pourcentage d'influence des munitions) + pourcentage d'influence des munitions * munitions/munitions max)
                                chunkValue += coefCase * givePrice((*it)->type)
                                                * (1+IMPACT_NBR_BALLE*((*it)->info/giveAmmo((*it)->type))-1);
                            }

                            //Valeur liée au sucre présent
                            //TODO: compter le nombre de grains de sucre sur la case
                            //chunckValue += coefCase * World::at(iVoisX,iVoisY)->sugar;

                            //Valeur liée aux bâtiments présents
                            int valueBat = 0;
                            if (chunk.building != NULL) {
                                switch(chunk.building->type)
                                {
                                    case BUILDING_VITAMIN_SOURCE :          valueBat = CONSTANTE_ARB_BUILDING_VITAMIN_SOURCE; break;
                                    case BUILDING_SUGAR_TREE :              valueBat = CONSTANTE_ARB_BUILDING_SUGAR_TREE; break;

                                        //Nos propres bâtiments
                                    case BUILDING_JUICE_BARREL :
                                        if(chunk.building == &ourJuiceBarrel)
                                            valueBat = CONSTANTE_ARB_BUILDING_JUICE_BARREL;
                                        break;
                                    case BUILDING_SUGAR_BOWL :
                                        if(chunk.building == &ourSugarBowl)
                                            valueBat = CONSTANTE_ARB_BUILDING_SUGAR_BOWL;
                                        break;
                                    case BUILDING_FRUCTIFICATION_TANK :
                                        if(chunk.building == &ourFructificationTank)
                                            valueBat = CONSTANTE_ARB_BUILDING_FRUCTIFICATION_TANK;
                                        break;
                                    default: valueBat = 0;
                                }

                                chunkValue += valueBat;
                            }
                        }
                    }
                }

                at(iX,iY).value_estimation = chunkValue;
            }
    }
}


void World::computeDanger()
{
    Fruit temp;

    for (signed int i = height - 1; i >= 0; i--) {
        for (signed int j = 0; j < width; j++) {
            at(j,i).danger_estimation = 0;
        }
    }

    for( map<int, Fruit>::iterator it = theirFruits.begin(); it != theirFruits.end(); it++ )
    {
        temp = it->second;

        for(int i = -1*RAYON_INFLUENCE_MOB;i <= RAYON_INFLUENCE_MOB; i++)
        {
            for(int j = -1*RAYON_INFLUENCE_MOB;j <= RAYON_INFLUENCE_MOB; j++)
            {
                if(exists(temp.position->X_coord+i,temp.position->Y_coord+j))
                    at(temp.position->X_coord+i,temp.position->Y_coord+j).danger_estimation = INFLUENCE_DANGERDIRECT * (RAYON_INFLUENCE_MOB+1-fabs((i*j)/2));
            }
        }
    }
}


int givePrice(int objet)
{
    switch(objet)
    {
        case EQUIPMENT_TEA_SPOON :      return 10;
        case EQUIPMENT_TOOTHPICK :      return 20;
        case EQUIPMENT_CUTTER :         return 50;
        case EQUIPMENT_LIGHTER :        return 15;
        case EQUIPMENT_LEMONER :        return 35;
        case EQUIPMENT_SALT_SNIPER :    return 60;
        case EQUIPMENT_PEELER :         return 15;
        case EQUIPMENT_JUICE_NEEDLE :   return 30;
        case EQUIPMENT_RELOADER :       return 20;
        default : return 0;
    }
}

int giveAmmo(int objet)
{
    switch(objet)
    {
        case EQUIPMENT_TEA_SPOON :      return -1;
        case EQUIPMENT_TOOTHPICK :      return -1;
        case EQUIPMENT_CUTTER :         return -1;
        case EQUIPMENT_LIGHTER :        return 100;
        case EQUIPMENT_LEMONER :        return 25;
        case EQUIPMENT_SALT_SNIPER :    return 10;
        case EQUIPMENT_PEELER :         return -1;
        case EQUIPMENT_JUICE_NEEDLE :   return 10;
        case EQUIPMENT_RELOADER :       return 1;
        default : return 0;
    }
}

World::World() {
    sugarCount = 0;
    vitaminCount = 0;
}

World::~World() {
}

bool World::isWalkable(map_chunk* chk){
    return (map_ctr<map_chunk>::isWalkable(chk) && (chk->fruit == NULL));
}

unsigned int World::getSugarCount() {
    return sugarCount;
}

unsigned int World::getVitaminCount() {
    return vitaminCount;
}

void printBuilding(Building * build) {
    cout << " Batiment [" << ai.api->decode(build->type) << "] (";
    cout << build->position->X_coord << "," << build->position->Y_coord << ")\n";
}

void printFruit(Fruit * fruit) {
    cout << " Fruit [" << ai.api->decode(fruit->type) << "] (";
    cout << fruit->position->X_coord << "," << fruit->position->Y_coord << ")\n";
}

void printObject(Object * objet) {
    cout << " Objet [" << ai.api->decode(objet->type) << "] (";
    cout << objet->position->X_coord << "," << objet->position->Y_coord << ") : ";
    cout << objet->info << "\n";
}

void World::printInfos () {
    cout << "Informations sur la configuration actuelle :\n";
    cout << "\n" << nOpponents << " ennemi(s)\n";

    cout << "Limites : " << maxCherries << " cerise(s), " << maxKiwis;
    cout << " kiwi(s), " << maxNuts << " noix(s) (haha)" << endl;

    cout << "\nNos Fruits :\n";
    map<int, Fruit>::iterator fruitIt;
    for (fruitIt = ourFruits.begin(); fruitIt != ourFruits.end(); fruitIt++)
        printFruit(&(fruitIt->second));

    fflush(stdout);

    cout << "\nNotre base :\n";
    printBuilding(&ourFructificationTank);
    printBuilding(&ourJuiceBarrel);
    printBuilding(&ourSugarBowl);

    fflush(stdout);
    cout << "\nBatiments adverses :\n";
    vector<Building>::iterator batIt;
    for (batIt = theirBuildings.begin(); batIt != theirBuildings.end(); batIt++)
        printBuilding(&(*batIt));

    cout << "\nEt pour finir, la map ! :\n";
    if (height == 0) return;
    cout << width << "*" << height << endl;
    for (signed int i = height - 1; i >= 0; i--) {
        for (signed int j = 0; j < width; j++) {
            if (at(j,i).objects.size() != 0) {
                cout << "E";
            } else if (at(j,i).building != NULL) {
                cout << "B";
            } else if (at(j,i).fruit != NULL) {
                cout << "F";
            } else if (at(j,i).walkable == false) {
                cout << "#";
            } else {
                cout << ".";
            }
            fflush(stdout);
        }
        cout << i << "\n";
    }
    fflush(stdout);
}

bool World::updateSugarCount(signed int val) {
    //Teste le cas où l'on retire plus de sucre qu'on en a :
    //si le jeu accepte, on a un problème de comptage !!
    if ((signed int)sugarCount < -val) {
        return false;
    }
    sugarCount += val;
    return true;
}

bool World::updateVitaminCount(signed int val) {
    //Teste le cas où l'on retire plus de vitamine qu'on en a :
    //si le jeu accepte, on a un problème de comptage !!
    if ((signed int)vitaminCount < -val) {
        return false;
    }
    vitaminCount += val;
    return true;
}

//Fonction appelée dans la méthode initGame ; remplit les infos du monde
void World::initWorld(IntMatrix2 architecture,
		IntMatrix2 fruits, IntMatrix2 buildings,
		int limitCherry, int limitKiwi, int limitNut,
		int vitaminGoal, int maxNbTurns) {

    //Conditions de victoire
    ai.limVitamines = vitaminGoal;
    ai.limTurns = maxNbTurns;
    ai.curTurn = 0;

    //Initialise la carte
    width = architecture.size();
    height = architecture[0].size();
    content = new map_chunk[width * height];

	//Chargement de nos batiments
	for (int i = 0; i < buildings.size(); i++) {
	    Building leBuilding;
	    leBuilding.position = &this->at(buildings[i][OBJECT_X],buildings[i][OBJECT_Y]);
	    leBuilding.type = buildings[i][OBJECT_TYPE];
	    leBuilding.id = buildings[i][OBJECT_ID];

	    //printBuilding(&leBuilding);
	    switch(leBuilding.type) {
            case BUILDING_FRUCTIFICATION_TANK:
            ourFructificationTank = leBuilding;
            this->at(buildings[i][OBJECT_X],buildings[i][OBJECT_Y]).building = &ourFructificationTank;
            break;
            case BUILDING_JUICE_BARREL:
            ourJuiceBarrel = leBuilding;
            this->at(buildings[i][OBJECT_X],buildings[i][OBJECT_Y]).building = &ourJuiceBarrel;
            break;
            case BUILDING_SUGAR_BOWL:
            ourSugarBowl = leBuilding;
            this->at(buildings[i][OBJECT_X],buildings[i][OBJECT_Y]).building = &ourSugarBowl;
            break;
        }

	}

	//Chargement de nos fruits
	for (int i = 0; i < fruits.size(); i++) {
        Fruit leFruit;
        leFruit.position = &this->at(fruits[i][OBJECT_X],fruits[i][OBJECT_Y]);
        leFruit.id = fruits[i][OBJECT_ID];
        leFruit.type = fruits[i][OBJECT_TYPE];
        leFruit.init();
        ourFruits[leFruit.id] = leFruit;
        this->at(fruits[i][OBJECT_X],fruits[i][OBJECT_Y]).fruit = &ourFruits[leFruit.id];
	}

    //Chargement de l'architecture de la map
    for (int iCol = 0; iCol < architecture.size(); iCol++) {
        for (int iLine = 0; iLine < architecture[0].size(); iLine++) {
            int laCase = architecture[iCol][iLine];
            //Indique si la case est traversable (rien) ou non (mur ou batiment)
            this->at(iCol, iLine).walkable = (laCase == NOTHING);
            //Initialise la position de la case
            this->at(iCol, iLine).X_coord = iCol;
            this->at(iCol, iLine).Y_coord = iLine;
            //Test : si on a un batiment, plusieurs cas se présententent
            if (laCase != NOTHING && laCase != WALL) {
                //Si le pointeur vers batiment est nul, c'est que ce batiment ne nous appartient pas
                //(neutre ou ennemi)
                if (this->at(iCol, iLine).building == NULL) {
                    Building leBuilding;
                    leBuilding.id = 0;  //ID non renseignée
                    leBuilding.position = &this->at(iCol, iLine);
                    leBuilding.type = laCase;
                    switch(laCase) {
                    case BUILDING_SUGAR_TREE:
                    theSugarTree = leBuilding;
                    this->at(iCol, iLine).building = &theSugarTree;
                    break;
                    case BUILDING_VITAMIN_SOURCE:
                    theVitaminSource = leBuilding;
                    this->at(iCol, iLine).building = &theVitaminSource;
                    break;
                    default:
                    theirBuildings.push_back(leBuilding);
                    this->at(iCol, iLine).building = &theirBuildings.back();
                    break;
                    }
                }
            }
        }
    }

    //Calcul du nombre d'ennemis
	nOpponents = theirBuildings.size() / 3;

    //Définition des limites en fruits
    maxKiwis = limitKiwi;
    maxCherries = limitCherry;
    maxNuts = limitNut;

#if DEBUG_INIT_WORLD
    printInfos();
#endif

}

vector<Object*>::iterator getIteratorToObject(int objectId, vector<Object*>& vec) {
    vector<Object*>::iterator it = vec.begin();
    while (it != vec.end() && (*it)->id != objectId) {
        it++;
    }
    return (it - 1);
}


//Fonction appelée au début de playTurn : met à jour les infos du monde
void World::updateAtBeginningOfTurn(IntMatrix2& newObjects,
    IntMatrix1& deletedObjects, IntMatrix2& movedObjects,
    IntMatrix2& modifiedFruits, IntMatrix2& modifiedSugarDrops) {

    ai.curTurn++;
    vitaminCount++;

    //pour les nouveaux objets et les sugdrops, on se fait pas chier, on appelle la fonction
    //anciennement chestOpen
    updateDuringTurn (newObjects, modifiedSugarDrops);

    //Gestion des objets supprimés
    for (int i = 0; i < deletedObjects.size(); i++) {
        map<int, Fruit>::iterator delFruit;
        map<int, Object>::iterator delObject;
        if ((delObject = items.find(deletedObjects[i])) != items.end()) {
            //Retire l'objet du monde et de sa case
#if DEBUG_TURN
            cout << " Del ";
            printObject(&delObject->second);
#endif
            //Cas du sucre : normalement 1 sugardrop/case ?
            if (delObject->second.type == SUGAR_DROP) {
                this->at(newObjects[i][OBJECT_X],
                     newObjects[i][OBJECT_Y]).valeurSucre = 0;
            }
            delObject->second.position->objects.erase(
                getIteratorToObject(deletedObjects[i], delObject->second.position->objects));
            items.erase(delObject);
        }
        else if ((delFruit = ourFruits.find(deletedObjects[i])) != ourFruits.end()) {
            //Retire le fruit du monde et de sa case
#if DEBUG_TURN
            cout << " RIP ";
            printFruit(&delFruit->second);
#endif
            delFruit->second.position->fruit = NULL;
            ourFruits.erase(delFruit);
        }
        else if ((delFruit = theirFruits.find(deletedObjects[i])) != theirFruits.end()) {
 #if DEBUG_TURN
            cout << " Killed ";
            printFruit(&delFruit->second);
#endif
            delFruit->second.position->fruit = NULL;
            theirFruits.erase(delFruit);
        }
    }

    //Gestion des déplacements
    //ici a priori on ne verra que les fruits ennemis
    for (int i = 0; i < movedObjects.size(); i++) {
        int oldX = movedObjects[i][OBJECT_X];
        int oldY = movedObjects[i][OBJECT_Y];
        int newX = movedObjects[i][OBJECT_NEW_X];
        int newY = movedObjects[i][OBJECT_NEW_Y];
        cout << " mov (" << oldX << "," << oldY << ") to (";
        cout << newX << "," << newY << ")";

        if (oldX == newX && oldY == newY) continue;
        //Teste si l'objet déplacé est le fruit ou un item de la case
        if (at(oldX, oldY).fruit != NULL && at(oldX, oldY).fruit->id == movedObjects[i][OBJECT_ID]) {
            //met à jour la position du fruit dans la table correspondante
            at(oldX, oldY).fruit->position = &at(newX, newY);
            //échange les pointeurs des deux cases
            at(newX, newY).fruit = at(oldX, oldY).fruit;
            at(oldX, oldY).fruit = NULL;
        } else {
            //Récupère le pointeur sur l'objet dans la table items
            vector<Object*>::iterator it = getIteratorToObject(movedObjects[i][OBJECT_ID], at(oldX, oldY).objects);
            //Met à jour sa position
            (*it)->position = &at(newX, newY);
            //Le change de case
            at(newX, newY).objects.push_back(*it);
            at(oldX, oldY).objects.erase(it);
        }
    }

    //Gestion des fruits modifiés
    //ici on verra nos fruits en 1v1 mais aussi ceux des adversaires qui s'entretuent
    for (int i = 0; i < modifiedFruits.size(); i++) {
        map<int, Fruit>::iterator modFruit;
        if ((modFruit = ourFruits.find(modifiedFruits[i][OBJECT_ID])) != ourFruits.end()) {
            modFruit->second.life = modifiedFruits[i][OBJECT_LIFE];
            modFruit->second.defense = modifiedFruits[i][OBJECT_DEFENSE];
        }
        else if ((modFruit = theirFruits.find(modifiedFruits[i][OBJECT_ID])) != theirFruits.end()) {

        }
#if DEBUG_TURN
        cout << " Mod ";
        printFruit(&modFruit->second);
#endif
    }

    computeDanger();
    computeValue();
}

//Fonction appelée au cours d'un tour (ex: kill ou ouverture de coffre)
void World::updateDuringTurn(IntMatrix2& newObjects, IntMatrix2& modifiedSugarDrops) {
	//Chargement des nouveaux objets
	for (int i = 0; i < newObjects.size(); i++) {
        fflush(stdout);
#if DEBUG_TURN
    cout << "Add ";
#endif
	    switch(newObjects[i][OBJECT_TYPE]) {
	        //Gestion des fruits
            case FRUIT_CHERRY:
            case FRUIT_KIWI:
            case FRUIT_NUT: {
                Fruit leFruit;
                leFruit.id = newObjects[i][OBJECT_ID];
                leFruit.type = newObjects[i][OBJECT_TYPE];
                leFruit.position = &this->at(newObjects[i][OBJECT_X],newObjects[i][OBJECT_Y]);
                leFruit.init();
                theirFruits[leFruit.id] = leFruit;
                this->at(newObjects[i][OBJECT_X],newObjects[i][OBJECT_Y]).fruit = &theirFruits[leFruit.id];
#if DEBUG_TURN
                cout << " Ennemi ";
                fflush(stdout);
                printFruit(&leFruit);
                fflush(stdout);
#endif
                break;
            }
            //Gestion du sucre
            /*case SUGAR_DROP:
            this->at(newObjects[i][OBJECT_X],
                     newObjects[i][OBJECT_Y]).valeurSucre += newObjects[i][OBJECT_INFO];
            cout << " Sucre @ (" << newObjects[i][OBJECT_X] << "," <<
                    newObjects[i][OBJECT_Y] << ")=" << this->at(newObjects[i][OBJECT_X],
                     newObjects[i][OBJECT_Y]).valeurSucre << endl;
            break;*/
            //Gestion des autres objets (coffres et équipement)
            default: {
                Object lObjet;
                lObjet.id = newObjects[i][OBJECT_ID];
                lObjet.type = newObjects[i][OBJECT_TYPE];
                lObjet.info = newObjects[i][OBJECT_INFO];
                lObjet.position = &this->at(newObjects[i][OBJECT_X],newObjects[i][OBJECT_Y]);
                items[lObjet.id] = lObjet;
#if DEBUG_TURN
                printObject(&lObjet);
#endif
                //Cas du sucre : on met la valeur dans les propriétés de la case pour éviter un parcours
                if (newObjects[i][OBJECT_TYPE] == SUGAR_DROP) {
                    this->at(newObjects[i][OBJECT_X],
                         newObjects[i][OBJECT_Y]).valeurSucre = newObjects[i][OBJECT_INFO];
                }
                //On stocke l'objet dans la liste de sa case
                this->at(newObjects[i][OBJECT_X],
                         newObjects[i][OBJECT_Y]).objects.push_back(&items[lObjet.id]);
                break;
            }
	    }
	}

    //Gestion des sugar drops modifiés
    for (int i = 0; i < modifiedSugarDrops.size(); i++) {
        map<int, Object>::iterator modSugar = items.find(modifiedSugarDrops[i][OBJECT_ID]);
        modSugar->second.info = modifiedSugarDrops[i][OBJECT_SUGAR];
        modSugar->second.position->valeurSucre = modSugar->second.info;
#if DEBUG_TURN
        cout << " ModSug ";
        printObject(&modSugar->second);
#endif
    }
}

void World::getNeighborhood( pfNode<map_chunk>* node, list< pfNode<map_chunk>* >* neighborhood){
    map_ctr<map_chunk>::getNeighborhood( node, neighborhood );
    for( list< pfNode< map_chunk >* >::iterator it = neighborhood->begin(); it != neighborhood->end(); it++ ){
        (*it)->danger = (*it)->elem->danger_estimation;
        (*it)->value = (*it)->elem->value_estimation;
    }
}

static void spread(int spreadval, areas type, map_chunk* center, vector<map_chunk*>* vect, map_ctr<map_chunk>* ctr){
    if( spreadval > AREA_SPREAD ) return;
    spreadval++;
    map_chunk* target;
    bool found;

    //EAST
    if( ctr->exists( center->X_coord+1, center->Y_coord ) ){
        target = &ctr->at( center->X_coord+1, center->Y_coord );
        found = false;
        for( vector<map_chunk*>::iterator it = vect->begin(); it < vect->end() && !found; it++ ){
            if( *it == target ) found = true;
        }
        if( !found ){
            vect->push_back(target);
            target->area_qualif |= type;
        }
        spread(spreadval,type,target,vect,ctr);
    }

    //WEST
    if( ctr->exists( center->X_coord-1, center->Y_coord ) ){
        target = &ctr->at( center->X_coord-1, center->Y_coord );
        found = false;
        for( vector<map_chunk*>::iterator it = vect->begin(); it < vect->end() && !found; it++ ){
            if( *it == target ) found = true;
        }
        if( !found ){
            vect->push_back(target);
            target->area_qualif |= type;
        }
        spread(spreadval,type,target,vect,ctr);
    }

    //SOUTH
    if( ctr->exists( center->X_coord, center->Y_coord+1 ) ){
        target = &ctr->at( center->X_coord, center->Y_coord+1 );
        found = false;
        for( vector<map_chunk*>::iterator it = vect->begin(); it < vect->end() && !found; it++ ){
            if( *it == target ) found = true;
        }
        if( !found ){
            vect->push_back(target);
            target->area_qualif |= type;
        }
        spread(spreadval,type,target,vect,ctr);
    }

    //NORTH
    if( ctr->exists( center->X_coord, center->Y_coord-1 ) ){
        target = &ctr->at( center->X_coord, center->Y_coord-1 );
        found = false;
        for( vector<map_chunk*>::iterator it = vect->begin(); it < vect->end() && !found; it++ ){
            if( *it == target ) found = true;
        }
        if( !found ){
            vect->push_back(target);
            target->area_qualif |= type;
        }
        spread(spreadval,type,target,vect,ctr);
    }
}
void World::makeAreas(){
    //Notre base
    map_chunk* root = ourJuiceBarrel.position;
    ourbase.push_back( root ); //On ajoute le bâtiment à la zone
    spread(0,area_ourbase,root,&ourbase,this);
    root = ourSugarBowl.position;
    ourbase.push_back( root );
    spread(0,area_ourbase,root,&ourbase,this);
    root = ourFructificationTank.position;
    ourbase.push_back( root );
    spread(0,area_ourbase,root,&ourbase,this);

    //L'arbre à sucre
    root = theSugarTree.position;
    sugartree.push_back( root ); //On ajoute le bâtiment à la zone
    spread(0,area_sugartree,root,&sugartree,this);

    //La fontaine
    root = theVitaminSource.position;
    vitsource.push_back( root ); //On ajoute le bâtiment à la zone
    spread(0,area_vitsource,root,&vitsource,this);
}

int World::fructify(int fruitType, int x, int y) {
    int res = ai.api->fructify(fruitType, x, y);
    if (res >= 0) {
        //Tout s'est bien passé, on a créé un fruit !
        Fruit leFruit;
        leFruit.type = fruitType;
        leFruit.id = res;
        leFruit.position = &at(x,y);
        ourFruits[leFruit.id] = leFruit;
        leFruit.position->fruit = &ourFruits[leFruit.id];
    } else {
        cout << ai.api->decode(res) << endl;
    }
    return res;
}
